/*
 * Sniff.java
 *
 * Created on August 21, 2007, 5:55 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.xeerkat.xmpp.tools;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URI;
import java.util.logging.Logger;
import org.infoset.xml.DestinationSource;
import org.infoset.xml.Element;
import org.infoset.xml.Infoset;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.Item;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.WriterItemDestination;
import org.xeerkat.xmpp.ClientConnection;
import org.xeerkat.xmpp.ID;
import org.xeerkat.xmpp.Protocol;

/**
 *
 * @author alex
 */
public class Roster implements Runnable
{
   
   static Logger LOG = Logger.getLogger(Sniff.class.getName());
   /** Creates a new instance of Sniff */
   ID id;
   ClientConnection connection;
   public Roster(ID id)
      throws javax.naming.NamingException
   {
      this.id = id;
      connection = ClientConnection.lookupByDomain(id.getDomain());
   }
   
   public void run() {
      try {
         
         connection.connect(id);

         if (!connection.isConnected()) {
            LOG.info("Login failed, exiting.");
            return;
         }

         connection.startReceiver(new DestinationSource() {
            public ItemDestination getDestination()
               throws XMLException 
            {
               final StringWriter w = new StringWriter();
               return new WriterItemDestination(w,"UTF-8") {
                  int level = 0;
                  public void send(Item item)
                     throws XMLException
                  {
                     switch (item.getType()) {
                        case ElementItem:
                           level++;
                           break;
                     }
                     super.send(item);
                     switch (item.getType()) {
                        case ElementEndItem:
                           level--;
                           if (level==0) {
                              LOG.info(w.toString());
                           }
                     }
                  }
               };
            }

            public ItemDestination getDestination(URI baseURI)
               throws XMLException {
               return getDestination();
            }

            public ItemConstructor createItemConstructor() {
               return InfosetFactory.getDefaultInfoset().createItemConstructor();
            }

            public Infoset getInfoset() {
               return InfosetFactory.getDefaultInfoset();
            }
         });
         connection.sendPresence(true);
         connection.setAutoReconnect(true);
         connection.startKeepAlive();
         
         BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
         boolean stop = false;
         System.out.println("Online!");
         do {
            String line = reader.readLine();
            if (line==null) {
               continue;
            }
            line = line.trim();
            if (line.equals("exit")) {
               stop = true;
            } else if (line.length()>0) {
               switch (line.charAt(0)) {
                  case '+':
                     // add a contact
                  {
                     String command = line.substring(1).trim();
                     String [] parts = command.split(" ");
                     try {
                        ID contactId = new ID(parts[0]);
                        String [] groups = new String[parts.length-1];
                        System.arraycopy(parts, 1, groups, 0, groups.length);
                        addContact(contactId,groups);
                     } catch (IllegalArgumentException ex) {
                        System.err.println("Bad ID "+parts[0]+": "+ex.getMessage());
                     }
                  }
                     break;
                  case '-':
                  {
                     String command = line.substring(1).trim();
                     try {
                        ID contactId = new ID(command);
                        removeContact(contactId);
                     } catch (IllegalArgumentException ex) {
                        System.err.println("Bad ID "+command+": "+ex.getMessage());
                     }
                  }
                     // remove a contact
                     break;
                  case '?':
                     // list the contacts
                     listRoster();
                     break;
                  case '>':
                     // request a subscription
                  {
                     String command = line.substring(1).trim();
                     try {
                        ID contactId = new ID(command);
                        subscribeContact(contactId);
                     } catch (IllegalArgumentException ex) {
                        System.err.println("Bad ID "+command+": "+ex.getMessage());
                     }
                  }
                     break;
                  case '<':
                     // approve a subscription
                  {
                     String command = line.substring(1).trim();
                     try {
                        ID contactId = new ID(command);
                        subscribedContact(contactId);
                     } catch (IllegalArgumentException ex) {
                        System.err.println("Bad ID "+command+": "+ex.getMessage());
                     }
                  }
             
               }
            }
         } while (!stop);
         connection.sendPresence(false);
         connection.stopKeepAlive();
         System.out.println("Disconnecting...");
         connection.disconnect();
         connection.stopReceiver();
      } catch (Exception ex) {
         ex.printStackTrace();
      }
   }
   
   public void listRoster() 
      throws XMLException
   {
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      ItemDestination dest = connection.getStanzaDestination();
      Element iq = constructor.createElement(Protocol.IQ);
      iq.setAttributeValue("from",connection.getBindID().toString());
      iq.setAttributeValue("type","get");
      iq.setAttributeValue("id","list");
      dest.send(iq);
      dest.send(constructor.createElement(Protocol.ROSTER_QUERY));
      dest.send(constructor.createElementEnd(Protocol.ROSTER_QUERY));
      dest.send(constructor.createElementEnd(Protocol.IQ));
   }
   
   public void addContact(ID contactId,String [] group) 
      throws XMLException
   {
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      ItemDestination dest = connection.getStanzaDestination();
      Element iq = constructor.createElement(Protocol.IQ);
      iq.setAttributeValue("from",connection.getBindID().toString());
      iq.setAttributeValue("type","set");
      iq.setAttributeValue("id","add");
      dest.send(iq);
      dest.send(constructor.createElement(Protocol.ROSTER_QUERY));
      Element item = constructor.createElement(Protocol.ROSTER_ITEM);
      item.setAttributeValue("jid", contactId.toString());
      dest.send(item);
      for (int i=0; i<group.length; i++) {
         dest.send(constructor.createElement(Protocol.ROSTER_GROUP));
         dest.send(constructor.createCharacters(group[i]));
         dest.send(constructor.createElementEnd(Protocol.ROSTER_GROUP));
      }
      dest.send(constructor.createElementEnd(Protocol.ROSTER_ITEM));
      dest.send(constructor.createElementEnd(Protocol.ROSTER_QUERY));
      dest.send(constructor.createElementEnd(Protocol.IQ));
   }
   
   public void removeContact(ID contactId) 
      throws XMLException
   {
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      ItemDestination dest = connection.getStanzaDestination();
      Element iq = constructor.createElement(Protocol.IQ);
      iq.setAttributeValue("from",connection.getBindID().toString());
      iq.setAttributeValue("type","set");
      iq.setAttributeValue("id","remove");
      dest.send(iq);
      dest.send(constructor.createElement(Protocol.ROSTER_QUERY));
      Element item = constructor.createElement(Protocol.ROSTER_ITEM);
      item.setAttributeValue("jid", contactId.toString());
      item.setAttributeValue("subscription", "remove");
      dest.send(item);
      dest.send(constructor.createElementEnd(Protocol.ROSTER_ITEM));
      dest.send(constructor.createElementEnd(Protocol.ROSTER_QUERY));
      dest.send(constructor.createElementEnd(Protocol.IQ));
   }
   
   public void subscribeContact(ID contactId) 
      throws XMLException
   {
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      ItemDestination dest = connection.getStanzaDestination();
      Element presence = constructor.createElement(Protocol.PRESENCE);
      presence.setAttributeValue("type","subscribe");
      presence.setAttributeValue("to",contactId.toString());
      dest.send(presence);
      dest.send(constructor.createElementEnd(Protocol.PRESENCE));
   }
   public void subscribedContact(ID contactId) 
      throws XMLException
   {
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      ItemDestination dest = connection.getStanzaDestination();
      Element presence = constructor.createElement(Protocol.PRESENCE);
      presence.setAttributeValue("type","subscribed");
      presence.setAttributeValue("to",contactId.toString());
      dest.send(presence);
      dest.send(constructor.createElementEnd(Protocol.PRESENCE));
   }
   public static void main(String [] args)
   {
      if (args.length<2) {
         System.err.println("Usage: "+Roster.class.getName()+" {id} {password}");
         System.exit(1);
      }
      try {
         ID id = new ID(args[0]);
         id.setPassword(args[1]);
         Roster roster = new Roster(id);
         roster.run();
      } catch (Exception ex) {
         ex.printStackTrace();
      }
   }
   
}
